package model;

import lang.Encoder;
import model.code.line.Line;
import model.code.line.LineList;
import model.code.line.NameLine;
import model.code.line.SingleLine;
import model.symbol.Scope;
import model.symbol.Symbol;
import observer.Copiable;
import observer.ObservedBoolean;
import observer.ObservedInt;
import observer.ObservedLong;

/**
 * This represents a segment of code, data, or some mix thereof.
 * @author Neil Dickson
*/
public class Segment implements Copiable<Segment>, Symbol {
	private long			id;
	private NameLine		name;
	private LineList		description;
	private ObservedInt		nBits;
	private	int				type;
	private ObservedLong	alignment;
	private ObservedLong	address;
	private ObservedLong	fileAlignment;
	private ObservedLong	fileAddress;
	private ObservedLong	maxSize;

	private ObservedBoolean isReadable;
	private ObservedBoolean isWritable;
	private ObservedBoolean isExecutable;
	private ObservedBoolean isShared;
	private ObservedBoolean isNoCache;
	private ObservedBoolean isNoPage;
	private ObservedBoolean isDiscardable;

	public static final int		DEFAULT_N_BITS		= 32;
	public static final int		DEFAULT_ALIGNMENT	= Encoder.SECTION_ALIGNMENT;
	public static final int		DEFAULT_FILE_ALIGNMENT	= Encoder.FILE_ALIGNMENT;
	public static final long	ANY_ADDRESS			= -1;
	public static final long	NO_MAX_SIZE			= -1;
	public static final int[]	POSSIBLE_N_BITS		= {16,32,64};
	public static final int[]	POSSIBLE_ALIGNMENTS	= {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192};

	public static final int		ANY_TYPE			= -1;
	public static final int		CODE				= 0;
	public static final int		INIT_DATA			= 1;
	public static final int		UNINIT_DATA			= 2;
	public static final int		READ_ONLY_DATA		= 3;

	public static final String	DEFAULT_CODE_SEG_NAME = ".text";
	public static final String	DEFAULT_INIT_DATA_SEG_NAME = ".data";
	public static final String	DEFAULT_UNINIT_DATA_SEG_NAME = ".bss";
	public static final String	DEFAULT_READ_ONLY_DATA_SEG_NAME = ".rdata";

	public Segment(String name) {
		this(name,DEFAULT_N_BITS,ANY_TYPE);
	}
	public Segment(String name,int nBits,int type) {
		this(name,nBits,type,DEFAULT_ALIGNMENT,ANY_ADDRESS,NO_MAX_SIZE,DEFAULT_FILE_ALIGNMENT,ANY_ADDRESS);
	}
	public Segment(String name,int nBits,int type,long alignment,long address,long maxSize,long fileAlignment,long fileAddress) {
		id = Scope.getUniqueID();
		this.name = new NameLine(name.toCharArray(),this);
		this.description = new SingleLine(Line.EMPTY_CHAR_ARRAY,null,0);
		this.nBits = new ObservedInt(nBits);
		this.type = type;
		this.alignment = new ObservedLong(alignment);
		this.address = new ObservedLong(address);
		this.fileAlignment = new ObservedLong(fileAlignment);
		this.fileAddress = new ObservedLong(fileAddress);
		this.maxSize = new ObservedLong(maxSize);
		isReadable = new ObservedBoolean(true);
		isWritable = new ObservedBoolean(type!=CODE && type!=READ_ONLY_DATA);
		isExecutable = new ObservedBoolean(type==ANY_TYPE || type==CODE);
		isShared = new ObservedBoolean(false);
		isNoCache = new ObservedBoolean(false);
		isNoPage = new ObservedBoolean(false);
		isDiscardable = new ObservedBoolean(false);
	}

	public Segment copy() {
		return new Segment(name.toString(),nBits.get(),type,alignment.get(),address.get(),maxSize.get(),fileAlignment.get(),fileAddress.get());
	}

	public long getID() {
		return id;
	}

	public NameLine getName() {
		return name;
	}

	public LineList getDescription() {
		return description;
	}

	public ObservedInt getnBits() {
		return nBits;
	}

	public ObservedLong getAlignment() {
		return alignment;
	}

	public ObservedLong getAddress() {
		return address;
	}

	public ObservedLong getMaxSize() {
		return maxSize;
	}

	public int getType() {
		return type;
	}

	public void setReadable(boolean isReadable) {
		this.isReadable.set(isReadable);
	}
	public void setWritable(boolean isWritable) {
		this.isWritable.set(isWritable);
	}
	public void setExecutable(boolean isExecutable) {
		this.isExecutable.set(isExecutable);
	}
	public void setShared(boolean isShared) {
		this.isShared.set(isShared);
	}
	public void setNoCache(boolean isNoCache) {
		this.isNoCache.set(isNoCache);
	}
	public void setNoPage(boolean isNoPage) {
		this.isNoPage.set(isNoPage);
	}
	public void setDiscardable(boolean isDiscardable) {
		this.isDiscardable.set(isDiscardable);
	}

	public ObservedLong getFileAddress() {
		return fileAddress;
	}

	public ObservedLong getFileAlignment() {
		return fileAlignment;
	}

	public boolean equals(Object o) {
		if (this==o) {
			return true;
		}
		if (!(o instanceof Segment)) {
			return false;
		}
		Segment that = (Segment)o;
		return name.equals(that.name)
				&& nBits.get()==that.nBits.get()
				&& type==that.type
				&& alignment.get()==that.alignment.get()
				&& address.get()==that.address.get()
				&& fileAlignment.get()==that.fileAlignment.get()
				&& fileAddress.get()==that.fileAddress.get()
				&& maxSize.get()==that.maxSize.get();
	}

	public void setType(int type) {
		this.type = type;
	}
}
